﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;


namespace Qetza.TeamFoundation.WorkItemQuery.Admin
{
    /// <summary>
    /// The work item query admin engine.
    /// </summary>
    public class WiqAdminEngine : IEngine, ICommandContainer
    {
        #region Fields

        // registered commands.
        private IDictionary<string, ICommand> m_commands;

        #endregion

        #region Constructors

        /// <summary>
        /// Initializes a new instance of the <see cref="WiqAdminEngine"/> class.
        /// </summary>
        /// <param name="validator">
        /// The parameter validator for validating command line parameters.
        /// </param>
        public WiqAdminEngine(IParameterValidator validator)
        {
            this.m_commands = new Dictionary<string, ICommand>();

            this.RegisterCommand(new CommandHelp(this, validator));
            this.ParameterValidator = validator;
        }

        #endregion

        #region Properties

        /// <summary>
        /// Gets the parameter validator.
        /// </summary>
        /// <value>
        /// The parameter validator.
        /// </value>
        public IParameterValidator ParameterValidator { get; private set; }

        #endregion

        #region IEngine Members

        /// <summary>
        /// Runs the workitem query engine.
        /// </summary>
        /// <param name="arguments">
        /// The process parameters.
        /// </param>
        /// <returns>
        /// <b>0</b> if no error occured; otherwise <b>1</b>;
        /// </returns>
        public int Run(string[] arguments)
        {
            try
            {
                // get command and parameters
                var command = this.GetCommand(arguments);
                if (command is CommandHelp)
                {
                    this.HandleHelpCommand(command, arguments);

                    return 0;
                }

                var parameters = this.ParseArguments(arguments);

                // validate command arguments
                this.ParameterValidator.Validate(
                    parameters, 
                    command.AllowedParameters,
                    command.ValidateParameters
                    );

                // execute command
                command.Execute(parameters);
            }
            catch (Exception ex)
            {
                Utility.WriteWrappedLines(ex.Message);
                Console.WriteLine(ResourceStrings.OperationFailed);

                return 1;
            }

            return 0;
        }

        #endregion

        #region ICommandContainer Members

        /// <inheritdoc />
        public IEnumerable<ICommand> RegisteredCommands
        {
            get { return this.m_commands.Values; }
        }

        /// <inheritdoc />
        /// <exception cref="ArgumentNullException">
        /// <paramref name="command"/> is a null reference (<b>Nothing</b> in VB.Net).
        /// </exception>
        public ICommandContainer RegisterCommand(ICommand command)
        {
            // validate parameter
            if (command == null)
            {
                throw new ArgumentNullException("command");
            }

            // register command
            this.m_commands[command.Name] = command;

            return this;
        }

        /// <inheritdoc />
        public ICommandContainer UnregisterCommand(string commandName)
        {
            this.m_commands.Remove(commandName);

            return this;
        }

        #endregion

        #region Private Helpers

        // Returns the called command.
        private ICommand GetCommand(string[] arguments)
        {
            if (arguments.Length == 0 || arguments[0] == "/?" || !this.m_commands.ContainsKey(arguments[0]))
            {
                return this.m_commands["help"];
            }

            return this.m_commands[arguments[0]];
        }

        // Handles help command.
        private void HandleHelpCommand(ICommand command, string[] arguments)
        {
            // parse help parameters
            var parameters = new Dictionary<string, string>();

            if (arguments.Length == 1)
            {
                // unknown command
                if (arguments[0] != "/?" && arguments[0] != command.Name)
                {
                    parameters[arguments[0]] = string.Empty;
                }
            }
            else if (arguments.Length > 1)
            {
                if (arguments[0] == "/?" || arguments[0] == command.Name)
                {
                    // help on specific command
                    parameters[arguments[1]] = string.Empty;
                }
                else
                {
                    // unknown command
                    parameters[arguments[0]] = string.Empty;
                }
            }

            command.Execute(parameters);
        }

        // Parses the specified arguments.
        private IDictionary<string, string> ParseArguments(string[] args)
        {
            var parameters = new Dictionary<string, string>();

            for (var i = 1 ; i < args.Length ; ++i)
            {
                // validate switch
                string[] values = null;
                var index = args[i].IndexOf(':');

                if (index > 0)
                {
                    values = new[] { args[i].Substring(0, index), args[i].Substring(index + 1) };
                }
                else
                {
                    values = new[] { args[i] };
                }

                if (!values[0].StartsWith("/"))
                {
                    throw new ArgumentException(string.Format(ResourceStrings.InvalidParam, values[0]));
                }

                if (values[0].Length == 1)
                {
                    throw new ArgumentException(string.Format(ResourceStrings.InvalidParam, values[0]));
                }

                if (parameters.ContainsKey(values[0]))
                {
                    throw new ArgumentException(string.Format(ResourceStrings.DuplicateSwitch, values[0]));
                }

                // set parameter
                parameters[values[0]] = (values.Length > 1 ? values[1] : string.Empty);
            }

            return parameters;
        }

        #endregion
    }
}
